#include "DEHT.h"
#include "misc.h"
#include "generator.h"
#include "Rainbow_Table.h"
#include <string.h>
#include <time.h>

#define BYTES_PER_VALIDATION_KEY 8

int main(int argc, char **argv){
	DEHT *ht = NULL;
	int hOutLen = 0;
	BasicHashFunctionPtr h = NULL;
	char pswdBuf[80] = {0};
	unsigned char hashBuf[80] = {0};
	unsigned char hashBuf2[80] = {0};
	char saltBuffer[6] = {0};
	LONG_INDEX_PROJ i=0 ,k = 0, PasswordRange=0;
	Rule *rule = NULL;	
	params ini;
	
	/* rainbow */
	int rainbowInfoSize = 0;
	rainbowTablePref rainbowInfo;

	int BUCKETS_IN_HASH_TABLE = 0;
	int PAIRS_PER_BUCKET =0;
	int CHAIN_LENGTH =0;

	/* read ini initialazation */
	initParams(&ini);

	/* seed the random */
	srand((unsigned int) time(0));
	
	if (argc != 4){ /* .ini file not loaded yet ! need to add */
		fprintf(stderr, "Error: Usage create_rainbow_table <hash> <filenames prefix> <preferences file>\n");
		return 0; 
	}

	/*parse the hash function*/
	parseHashFunction(argv[1], &h, &hOutLen);
	if (h == NULL){
		fprintf(stderr, "%s is not a known hash function\n", argv[3]);
		return 0;
	}
	
	/* read ini */
	if (! parseParamFile(argv[3], &ini)){
		fprintf(stderr, "Bad pref. file\n");
		return 0;
	}

	CHAIN_LENGTH = ini.chainLength;
	BUCKETS_IN_HASH_TABLE = ini.numBuckets;
	PAIRS_PER_BUCKET = ini.pairs;

	rainbowInfo.chainLength = CHAIN_LENGTH;

	/*parse rule and dictionary from ini*/
	rule = PPRule(ini.dictFile, ini.rule);
	if (rule == NULL){
		fprintf(stderr, "mem. allocation failure\n");
		return 0;
	}

	/* calc needed size */
	rainbowInfoSize = calcSizeNeeded(&rainbowInfo, rule);
	if (!(rainbowInfo.seedArr = create_seedArr(rainbowInfo.chainLength)) ){
		fprintf(stderr, "seed mem alloc. failure\n");
		free_PPRule(rule);
		return 0;
	}

	/*create a new DEHT, and work in cache mode (use read and calc)*/
	ht = create_empty_DEHT(argv[2], hash1,  hash2, argv[1], BUCKETS_IN_HASH_TABLE, PAIRS_PER_BUCKET, BYTES_PER_VALIDATION_KEY, rainbowInfoSize);
	if (ht == NULL) {
		fprintf(stderr, "Error: failed creating DEHT\n");
		free_PPRule(rule);
		free(rainbowInfo.seedArr);
		return 0;
	}

	if (read_DEHT_pointers_table(ht) == DEHT_STATUS_FAIL){
		free(rainbowInfo.seedArr);
		free_PPRule(rule);
		lock_DEHT_files(ht);
		return 0;
	}

	if (calc_DEHT_last_block_per_bucket(ht) == DEHT_STATUS_FAIL){
		free(rainbowInfo.seedArr);
		free_PPRule(rule);
		lock_DEHT_files(ht);
		return 0;
	}

	/* write info */
	if (!(writeRainbowDataToDEHT(ht, &rainbowInfo, rule) )){
		fprintf(stderr, "disk writing failure\n");
		free(rainbowInfo.seedArr);
		free_PPRule(rule);
		lock_DEHT_files(ht);
		return 0;
	}

	PasswordRange = (LONG_INDEX_PROJ) (rule->blockRange[rule->numBlocks-1])*10/((LONG_INDEX_PROJ)rainbowInfo.chainLength);

	/*Main Loop - inserting data*/
	for (i = 0; i < PasswordRange ; i++){
		createSalt(saltBuffer);
		k = pseudo_random_function((unsigned char *)saltBuffer ,6, i+pseudo_random_generator_proj(rand()) ); /* making a good random spread */
		generator(k, rule, pswdBuf);
		while (strcmp(pswdBuf,"") == 0){ /* avoiding the empty password */
			k++;
			generator(k, rule, pswdBuf);
		}
		cryptHash(h, pswdBuf, hashBuf);
		calc_down_Chain(ht, &rainbowInfo ,rule,(unsigned char *) hashBuf, rainbowInfo.chainLength-1 , (unsigned char *) hashBuf2);
		if (add_DEHT (ht, hashBuf2, hOutLen, (unsigned char *)pswdBuf, strlen(pswdBuf)) == DEHT_STATUS_FAIL){
			free(rainbowInfo.seedArr);
			free_PPRule(rule);
			lock_DEHT_files(ht);
			return 0;
		}
	}

	free(rainbowInfo.seedArr);
	free_PPRule(rule);
	lock_DEHT_files(ht);
	return 1;
}

/*checks if the Flag arg. is a valid input ("all" string or a positive integer)
 * if not a legal input return 0 (or if Flag is 0 <=> don't iterate)
 * if the string is all return -1
 * else return the integer
 */


